Wire pump BLE heartbeat to CGM reading schedule + preserve manager state on missing plugin#2461
Wire pump BLE heartbeat to CGM reading schedule + preserve manager state on missing plugin#2461loopkitdev wants to merge 3 commits into
Conversation
Send the pump a PumpHeartbeatRequest (last CGM reading date + expected reading interval) when it must provide the BLE heartbeat, or nil when the CGM wakes the app itself, via the new setBLEHeartbeatRequest API. The last-reading time is refreshed after every CGM reading (processCGMReadingResult already calls updatePumpManagerBLEHeartbeatPreference), so the pump's heartbeat cadence tracks the actual reading schedule.
instantiateDeviceManagers restored a manager by assigning pumpManager/cgmManager = the result of ...FromRawValue(...). When the plugin wasn't available in the running build, that returned nil, and the nil assignment fired the didSet, which wrote rawPumpManager/rawCGMManager = nil and DELETED the persisted state — so launching a build missing a plugin permanently wiped the pod pairing (incl. session keys) or CGM config. Only assign when the manager instantiates; otherwise log and leave the saved state on disk so a later build that includes the plugin can restore it.
TestI'm putting test notes in comments for this PR so they are in one place. Summary from initial testing: intermittent success
ConfigurationI started with LoopWorkspace next-dev commit 14b70bd. I am running 2 test phones:
Narrative
2nd gen, Omnipod 5 narrative
2026-07-19 06:16 lock 2nd gen (o5) phone 3rd gen, rPi DASH narrative
2026-07-19 06:16 lock 3rd gen (rPi DASH) phone |
getGlucoseSample() bridged async work to sync via DispatchGroup.wait(timeout: .distantFuture). It runs from update(), which executes on a Swift Concurrency cooperative-pool thread; blocking that thread starves the (core-count-sized) pool and deadlocks the whole concurrency runtime under repeated Live Activity updates (Loop stops looping). Observed on device as all cooperative threads stuck in getGlucoseSample -> dispatch_group wait, with the Nightscout upload queues blocked behind them. Make getGlucoseSample async and await getGlucoseSamples directly instead of blocking. Upstream fix (both sites) submitted as LoopKit#2465.
ConfigurationI was behind on configuration. Updated both Loop and OmnipodKit this morning.
Rebuilt onto the SE 2nd gen phone connected to an Omnipod 5 pod and then locked phone.
Will report back with longer term results. |
Test✅ successful test future tests:
ConfigurationThe basic configuration uses:
Customizations:
Test NarrativeI now have 3 phones using the latest version.
Log FilesIn each log file, the Build Details: buildDateString was used as a time flag to trim the contents in the
Statistics from Log FilesTable with statistics from the 3 test cases, where the csv files are filtered for deltaSec > 180, then statistics for that quantity reported. (This is a measure of the time between communications sessions between the phone and the pod.)
Personal LogThe truncated log file (18 hours of data): The csv file with the parsed Phone-Pod messages:
Test Phone, Omnipod 5 Log
The truncated log file (9 hours of data): The csv file with the parsed Phone-Pod messages: Test Phone, rPi DASH LogThe truncated log file (9 hours of data): The csv file with the parsed Phone-Pod messages: |
Test Fault✅ successfully triggered a fault in an actual Omnipod 5 Pod and fault was reported ConfigurationSE 2nd gen running the pod-connection-mgmt branch from my local clone with Omnipod 5 pod paired. NarrativeTest afternoon of 2026-07-21 Pod was within an hour of expiration
Observe looping on the Nightscout URL
I already broke the sound connection in case this pod faulted in the middle of the night. So I won't hear a fault.
Increase therapy settings:
More boluses
Log fileThis is the entire log of the pod life including times before the current version of code was installed
Loop Report test-o5-full-life-fault 2026-07-21 173319-0700.md Parsed messages from the full Omnipod 5 Pod life: |
|
Fixed in #2469 |
Summary
Wires the pump BLE heartbeat to the CGM reading schedule (uses the coupled LoopKit
setBLEHeartbeatRequestAPI) and hardens device-manager restore:DeviceDataManager.updatePumpManagerBLEHeartbeatPreferencesends aPumpHeartbeatRequest(last CGM reading date + expected interval) when the pump must provide the heartbeat, ornilwhen the CGM wakes the app itself; refreshed after every CGM reading.pumpManager/cgmManager = nil, whosedidSetdeleted the persisted state — permanently wiping the pod pairing (incl. session keys) or CGM config. Now it logs and leaves the saved state on disk for a later build that includes the plugin.Requires the LoopKit
setBLEHeartbeatRequestPR to compile; pairs with the OmnipodKit PR. Merge together. (Cross-links below.)🤖 Generated with Claude Code
https://claude.ai/code/session_017Tbv6GBAMmGATWp6zwBDy7
Coupled PRs — merge together (three-way dependency)
PumpManager.setBLEHeartbeatRequestprotocol APILoop and OmnipodKit will not compile without the LoopKit protocol change; land all three together and bump the LoopWorkspace next-dev submodule pins in one commit.